/*

    file:   window.h
    desc:   Window util heder.

    note:   Window provides interface between monitor and driver's screen memory (reference table).
            All windows are aligned horizontally, in order they were added with window_add(...).
            Each window can have following special parameters: vertical scroll (max 7 pixels) and 4bit color mask,
            which affects DDR of VIDEO_PORT.
            Window can either be invisible. In such case, it's videolines are not scanned - each line 
            means 1000 more free clock cycles.

    note:   Sum of window's heights should always be greater than vertical resolution. This is handled automatically until 
            number of used windows reaches MAX_WINDOWS.

    author: Jaromir Dvorak (md@unicode.cz)

    This file is part of the AVGA platform.
    http://avga.prometheus4.com/
    

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
*/



#ifndef _WINDOW_H_
#define _WINDOW_H_

#include "../config_utils.h"


/* ########## BEGIN OF USER CONFIGURABLE SECTION ########## */
#ifndef WINDOW_UTILCONFIG
#define WINDOW_UTILCONFIG

//
//Maximum count of windows on the screen.
//
#define MAX_WINDOWS		3

//
//define which types of scrolling to compile.
//
#define WINDOW_ABS_SCROLL
//#define WINDOW_SCROLL
//#define WINDOW_SCROLL_FULL


//
//enable this for ability to use individual tilest for each window.
//
//#define WINDOW_INDIVIDUAL_TILESET

//
//enable this for ability to use individual tilest for each window.
//
//#define WINDOW_INDIVIDUAL_REFTABLE

//
//reset window on video_init?
//
#define WINDOW_RESET

//
//enable individual window syncing
//
#define WINDOW_SYNC


#endif
/* ########### END OF USER CONFIGURABLE SECTION ########### */




/*
 *
 * Window handling.
 *
 */


struct window
{
	unsigned char line;
	unsigned char height;
	unsigned char params;

#ifdef WINDOW_INDIVIDUAL_TILESET
	unsigned char* pgmp;
#endif
#ifdef WINDOW_INDIVIDUAL_REFTABLE
	unsigned char* scrp;
#endif

} __attribute__((packed));

typedef struct window* WINDOW;



//
// Returns handle to the first window.
//
#define window_get_default()	({ extern struct window windows[]; windows; })



/*
 *
 * Generic window set-up.
 *
 */

//
// Creates a window.
//
WINDOW window_add(unsigned char line, unsigned char height, unsigned char params);

//
// Theese macros should be used instead of window_add.
//
#define window_add_visible(line, height)   window_add(line, height, 0xf8)
#define window_add_standard(row, rows)     window_add(DRIVER_ROW2Y(row), DRIVER_ROW2Y(rows), 0xf8)
#define window_add_space(height)           window_add(0, height, 0)

//
// Clears all visible window and restores original state (whole default reftable mapped to whole screen)
//
void window_reset();

#ifdef WINDOW_INDIVIDUAL_TILESET
#ifdef pgmpd
#undef pgmpd
#endif
unsigned char* _pgmp_default;
#define pgmpd _pgmp_default
#endif

#ifdef WINDOW_INDIVIDUAL_REFTABLE
#ifdef scrpd
#undef scrpd
#endif
unsigned char* _scrp_default;
#define scrpd _scrp_default
#endif

/*
 *
 * Basic window input/output macros.
 *
 */

//
// Window generic I/O
//
#ifdef WINDOW_INDIVIDUAL_TILESET
#define awindow_get_tileset(wnd)	    	(wnd->pgmp)
#define awindow_set_tileset(wnd, ptr)	    (wnd->pgmp = ptr)
#else
#define awindow_get_tileset(wnd)	    	(_pgmp)
#endif
#ifdef WINDOW_INDIVIDUAL_REFTABLE
#define awindow_get_reftable(wnd)	    	(wnd->scrp)
#define awindow_set_reftable(wnd, ptr)	    (wnd->scrp = ptr)
#else
#define awindow_get_reftable(wnd)	    	(_scrp)
#endif
#define awindow_get_row(wnd)                 DRIVER_Y2ROW(wnd->line)
#define awindow_get_startline(wnd)           ((wnd->line))
#define awindow_get_height(wnd)              DRIVER_Y2ROW(wnd->height)
#define awindow_get_num_rows(wnd)            awindow_get_height(wnd)
#define awindow_get_num_lines(wnd)           ((wnd->height))
#define awindow_get_scrollX(wnd)             ((wnd->params & 7))
#define awindow_get_scrollY(wnd)             DRIVER_Y2PRE(wnd->line)
#define awindow_get_ptr(wnd)                 (awindow_get_reftable(wnd)+awindow_get_row(wnd)*DRIVER_COLUMNS)
#define awindow_get_block_ptr(wnd, x, y)     (awindow_get_reftable(wnd)+x+(awindow_get_row(wnd)+y)*DRIVER_COLUMNS)
#define awindow_get_block(wnd, x, y) 	     (*awindow_get_block_ptr(wnd, x, y))
#define awindow_set_block(wnd, x, y, blk)    (awindow_get_block(wnd, x, y) = blk)
#define awindow_fill(wnd, blk)               memset(awindow_get_ptr(wnd), blk, (DRIVER_COLUMNS) * awindow_get_height(wnd))
#define awindow_fill_scroll(wnd, blk)        memset(awindow_get_ptr(wnd), blk, (DRIVER_COLUMNS) * (awindow_get_height(wnd)+1))
#define awindow_print(wnd, x, y, txt)        ({ strdump_P(awindow_get_block_ptr((wnd),(x),(y)), (txt)); })
#define awindow_print_P(wnd, x, y, txt)      ({ strdump(awindow_get_block_ptr((wnd),(x),(y)), (txt)); })
#define awindow_print_C(wnd, x, y, txt)      ({ awindow_print_P(wnd, x, y, PSTR(txt)); })


//
// Syncing to end of a window within one frame.
//
void awindow_sync(WINDOW wnd);

//
// Window clone.
//
#define awindow_add_clone(wnd)               window_add(wnd->line, wnd->height, wnd->params)
#define awindow_swap_content(wnd1, wnd2)     ({ void* __c = wnd1->scrp; wnd1->scrp=wnd2->scrp; wnd2->scrp=__c; })

//
// Window color effects.
//
void awindow_set_colormask(struct window* wnd, unsigned char mask);
#define awindow_show(wnd)  					 ({ wnd->params |= 0x08;  })
#define awindow_hide(wnd)  					 ({ wnd->params &= ~0x08; })

//
// Window scrolling effects.
//
void awindow_set_scroll(struct window* wnd, unsigned char x, unsigned char y);
void awindow_scroll(WINDOW wnd, signed char x, signed char y);
void awindow_scroll_full(struct window* wnd, signed char x, signed char y, unsigned char default_block);
#if DRIVER_BLOCK_HEIGHT == 8
#define awindow_fix(wnd)                     ({ wnd->height &= ~7; })
#define awindow_no_scroll(wnd)               ({ wnd->params &= ~7; wnd->line &= ~7; })
#else
#define awindow_fix(wnd)                     ({ wnd->height -= DRIVER_Y2PRE(wnd->height); })
#define awindow_no_scroll(wnd)               ({ wnd->params &= ~7; wnd->line -= DRIVER_Y2PRE(wnd->line); })
#endif
#define awindow_adjust_for_background(wnd, x, y)    awindow_set_scroll(wnd, ~(x), ~(y))

//
// Window transition effects (modification of window placement and dimensions)
// note: For scrolling, both wnd->line and wnd->height must be always multiply of 8.
//
#define awindow_set_startline(wnd, n)		 ({ wnd->line = n; })
#define awindow_move(wnd, n)            	 ({ wnd->line += n; })
#define awindow_set_height(wnd, n)		 ({ wnd->height = n; })
#define awindow_shrink(wnd, n)          	 ({ wnd->height += n; }) 




/*
 *
 * Optimized (selected) window input/output macros.
 *
 */

WINDOW _selwnd;
unsigned char *_selwndptr;

#define window_select(wnd)                  ({ _selwnd = wnd; _selwndptr = awindow_get_ptr(wnd); })
#define window_add_clone()                  awindow_add_clone(_selwnd)
#define window_swap_content(wnd2)           ({ awindow_swap_content(_selwnd, wnd2); _selwndptr = awindow_get_ptr(wnd2); })

#define window_get_reftable()               (_selwndptr)
#define window_set_reftable(ptr)            ({ _selwndptr = ptr; awindow_set_reftable(_selwnd, ptr); })
#define window_get_tileset()                awindow_get_tileset(_selwnd)
#define window_set_tileset(ptr)             awindow_set_tileset(_selwnd, ptr)
#define window_get_row()                    awindow_get_row(_selwnd)
#define window_get_startline()              awindow_get_startline(_selwnd)
#define window_get_height()                 awindow_get_height(_selwnd)
#define window_get_num_lines()              awindow_get_num_lines(_selwnd)
#define window_get_num_rows()               awindow_get_num_rows(_selwnd)
#define window_get_scrollX()                awindow_get_scrollX(_selwnd)
#define window_get_scrollY()                awindow_get_scrollY(_selwnd)
#define window_get_ptr()                    (_selwndptr)
#define window_get_block(x, y)              (_selwndptr[(x)+(y)*DRIVER_COLUMNS])
#define window_set_block(x, y, blk)         ({_selwndptr[(x)+(y)*DRIVER_COLUMNS] = (blk);})
#define window_fill(blk)                    ({memset(window_get_ptr(), blk, (DRIVER_COLUMNS) * window_get_height());})
#define window_fill_scroll(blk)             ({memset(window_get_ptr(), blk, (DRIVER_COLUMNS) * (window_get_height()+1));})
#define window_print(x, y, txt)             ({strdump(&window_get_block((x),(y)), (txt));})
#define window_print_P(x, y, txt)           ({strdump_P(&window_get_block((x), (y)), (txt));})
#define window_print_C(x, y, txt)           ({window_print_P(x, y, PSTR(txt));})

#define window_set_colormask(mask)          awindow_set_colormask(_selwnd, mask)
#define window_show()                       awindow_show(_selwnd)
#define window_hide()                       awindow_hide(_selwnd)

#define window_set_scroll(x,y)              awindow_set_scroll(_selwnd, x, y)
#define window_scroll(x,y)                  awindow_scroll(_selwnd, x, y)
#define window_scroll_full(x,y,blk)         awindow_scroll_full(_selwnd,x, y, blk)

#define window_adjust_for_background(x, y)  awindow_adjust_for_background(_selwnd, x, y)
#define window_no_scroll()                  awindow_no_scroll(_selwnd)
#define window_fix()                        awindow_fix(_selwnd)

#define window_set_startline(n)	            awindow_set_startline(_selwnd, n)
#define window_move(n)                      awindow_move(_selwnd, n)
#define window_set_height(n)	            awindow_set_height(_selwnd, n)
#define window_shrink(n)                    awindow_shrink(_selwnd, n)

#define window_sync()						awindow_sync(_selwnd);


#endif
